﻿namespace MicroCloud.Entity
{
    /// <summary>
    /// 实体查询器
    /// </summary>
    public class EntityQuerier : IEntityQuerier
    {
        #region "服务提供者"
        #region "获取 服务提供者"
        /// <summary>
        /// 获取 服务提供者
        /// </summary>
        protected IServiceProvider ServiceProvider { get; }
        #endregion
        #region "获取 日志记录器"
        /// <summary>
        /// 获取 日志记录器
        /// </summary>
        protected ILogger Logger => ServiceProvider.GetLogger(GetType());
        #endregion

        #region "获取 实体管理器"
        /// <summary>
        /// 获取 实体管理器
        /// </summary>
        protected IEntityManager EntityManager => ServiceProvider.GetService<IEntityManager>();
        #endregion

        #endregion

        #region "构造函数"
        #region "初始化一个实体查询器的新实例"
        /// <summary>
        /// 初始化一个实体查询器 <see cref="EntityQuerier"/> 的新实例
        /// </summary>
        /// <param name="provider">服务提供者</param>
        public EntityQuerier(IServiceProvider provider)
        {
            ServiceProvider = provider;
        }
        #endregion

        #endregion

        #region "方法"
        #region "获取指定实体类型所属的数据上下文实例"
        /// <summary>
        /// 获取指定实体类型所属的数据上下文实例
        /// </summary>
        /// <param name="entityType">实体类型</param>
        /// <returns>实体所属的数据上下文实例</returns>
        public virtual IDbContext GetEntityDbContext(Type entityType)
        {
            var unitOfWork = ServiceProvider.GetUnitOfWork();
            var dbContext = unitOfWork.GetEntityDbContext(entityType);
            return dbContext;
        }
        #endregion
        #region "获取指定类型实体的DbSet实例 <see cref="IQueryable{T}"/>，用于访问上下文和基础存储中给定类型的实体。"
        /// <summary>
        /// 获取指定类型实体的DbSet实例 <see cref="IQueryable{T}"/>，用于访问上下文和基础存储中给定类型的实体。
        /// </summary>
        /// <param name="entityType">实体类型</param>
        /// <returns>动态 <see cref="IQueryable{T}"/> 对象</returns>
        public virtual IQueryable<object> GetQueryable(Type entityType)
        {
            var queryable = GetEntityDbContext(entityType).GetQueryable(entityType);
            return queryable;
        }
        #endregion

        #region "获取指定实体类型名称所属的数据上下文实例"
        /// <summary>
        /// 获取指定实体类型名称所属的数据上下文实例
        /// </summary>
        /// <param name="entityTypeName">实体类型名称</param>
        /// <returns>实体所属的数据上下文实例</returns>
        public virtual IDbContext GetEntityDbContext(string entityTypeName)
        {
            var entityType = Type.GetType(entityTypeName);
            var dbContext = GetEntityDbContext(entityType);
            return dbContext;
        }
        #endregion
        #region "获取指定名称实体的DbSet实例 <see cref="IQueryable{T}"/>，用于访问上下文和基础存储中给定类型的实体。"
        /// <summary>
        /// 获取指定名称实体的DbSet实例 <see cref="IQueryable{T}"/>，用于访问上下文和基础存储中给定类型的实体。
        /// </summary>
        /// <param name="entityTypeName">实体类型名称</param>
        /// <returns>动态 <see cref="IQueryable{T}"/> 对象</returns>
        public virtual IQueryable<object> GetQueryable(string entityTypeName)
        {
            var entityType = Type.GetType(entityTypeName);
            var queryable = GetQueryable(entityType);
            return queryable;
        }
        #endregion

        #region "以字符串方式组装DbSet实例 <see cref="IQueryable{T}"/> Where部分表达式"
        /// <summary>
        /// 以字符串方式组装DbSet实例 <see cref="IQueryable{T}"/> Where部分表达式
        /// </summary>
        /// <param name="queryable">动态 <see cref="IQueryable{T}"/> 对象</param>
        /// <param name="wheres">
        /// Where部分表达式字符串
        /// <para>例："Field1.Contains(\"{value}\") and Field2.Equals(\"{value}\") and Field3 = {value}"，可传入数组自动拼接。</para>
        /// </param>
        /// <returns>动态 <see cref="IQueryable{T}"/> 对象</returns>
        public virtual IQueryable<object> AddWhere(IQueryable<object> queryable, params string[] wheres)
        {
            Check.NotNull(queryable, nameof(queryable));

            queryable = queryable.WhereAsString(wheres);

            return queryable;
        }
        #endregion
        #region "以字符串方式组装DbSet实例 <see cref="IQueryable{T}"/> Select部分表达式"
        /// <summary>
        /// 以字符串方式组装DbSet实例 <see cref="IQueryable{T}"/> Select部分表达式
        /// </summary>
        /// <param name="queryable">动态 <see cref="IQueryable{T}"/> 对象</param>
        /// <param name="selects">Select部分表达式字符串</param>
        /// <returns>动态 <see cref="IQueryable{T}"/> 对象</returns>
        public virtual IQueryable<object> AddSelect(IQueryable<object> queryable, params string[] selects)
        {
            Check.NotNull(queryable, nameof(queryable));

            queryable = queryable.SelectAsString(selects);

            return queryable;
        }
        #endregion

        #region "检查数据是否已被占用"
        /// <summary>
        /// 检查数据是否已被占用
        /// </summary>
        /// <typeparam name="TEntity">实体类型</typeparam>
        /// <param name="predicate">条件表达式</param>
        /// <param name="usedEntityType">占用数据的实体类型</param>
        /// <returns></returns>
        public bool CheckDataOccupied<TEntity>(Expression<Func<object, bool>> predicate, out Type usedEntityType)
        {
            usedEntityType = null;
            bool flag = false;

            var entityTypes = EntityManager.GetEntityTypes(type => type.IsBaseOn<TEntity>()).Distinct().ToArray();
            if (entityTypes?.Length > 0)
            {
                foreach (var entityType in entityTypes)
                {
                    var dbQueryable = GetQueryable(entityType);
                    //var count = dbQueryable.Where(predicate).Count();
                    //flag = count > 0;
                    flag = dbQueryable.Any(predicate);
                    if (flag)
                    {
                        usedEntityType = entityType;
                        break;
                    }
                }
            }
            return flag;
        }
        #endregion

        #endregion

    }
}
